import tkinter as tk
from datetime import time
from tkinter import ttk, messagebox
import time
from tkinter import filedialog
import ctypes
import chat.client.gol
from chat.client.utils.sender import socket_send_msg,socket_send_exit,socket_send_file,socket_send_rm_room
from chat.client.rooms.rooms import socket_send_userid, socket_get_room_history
import threading

winmm = ctypes.windll.winmm


'''
联系人窗口 名为 fri_form
聊天窗口 名为 app
为了解决奇怪的bug只能将聊天框与联系人窗口合并到此一个文件
初始化联系人窗口操作如下
所有聊天窗口操作均在 creat_chat()函数内
'''
def contact_cls_btn():
    '''
    此为关闭联系人窗口按钮相关函数
    :return:
    '''
    print('[前端] ==> 关闭联系人窗口')
    socket_send_exit()

    fri_form.quit()
    fri_form.destroy()
    time.sleep(0.1)
    exit(0)


'''初始化联系人窗口'''
fri_form = tk.Tk()
fri_form.title('联系人')

# 设定窗口
w = 400
h = 640
sw = fri_form.winfo_screenwidth()
sh = fri_form.winfo_screenheight()
x = (sw - w) / 2
y = (sh - h) / 2
fri_form.geometry("%dx%d+%d+%d" % (w, h, x, y))
fri_form.resizable(0, 0)
fri_form.protocol('WM_DELETE_WINDOW', contact_cls_btn)

'''设定联系人列表'''
fri_list = ttk.Treeview(fri_form, height=39, show="tree")
fri_list.place(x=10, y=30)

friend_list = []
group_list = []
friend_list_id = []
group_list_id = []
fri_tree = fri_list.insert('', 0, 'first', text='好友', values=('a'))
group_tree = fri_list.insert('', 1, 'second', text='群聊', values=("b"))
def flush_list():
    global fri_list
    global friend_list
    global fri_tree
    global group_list
    global group_tree
    global friend_list_id
    global group_list_id
    def get_friend(i):
        global friend_list
        global group_list
        '''
        得到好友信息的函数，后端需要进行更改，此为usr_name列表
        :return:
        '''
        friends = []
        user_info = chat.client.gol.get_value("user_info")
        for user in user_info:
            friends.append(user[i])

        # friends = ['f1', 'f2', 'f3', 'f4']
        return friends

    def get_group(i):
        global friend_list
        global group_list
        global friend_list_id
        global group_list_id
        '''
        得到好友信息的函数，后端需要进行更改，此为group_name列表
        :return:
        '''
        group = []
        room_info = chat.client.gol.get_value("room_info")
        for room in room_info:
            group.append(room[i])
        # group = ['a1', 'b2', 'c3', 'd4']
        return group


    friend_list = get_friend(1)
    group_list = get_group(1)
    friend_list_id = get_friend(0)
    group_list_id = get_group(0)
    print("[前端 friend_list]==> ", friend_list)
    print("[前端 group_list]==> ", group_list)


    '''
    添加联系人，目前为手动添加,插入操作为 fri_list.insert（）,
    value值在此处为一个列表,所以后端如果要定义使用usr_id,方便可以使用同一列表里的values[0]值,value[0]值可自行更改,如下，values[0]在插入时已赋值
    好友树与列表，fri_tree1为总栏
    群组树与列表，fri_tree2为总栏
    '''
    # fri_list.clear()
    fri_list.delete('first')
    fri_list.delete('second')
    fri_tree = fri_list.insert('', 0, 'first', text='好友', values=('a'))
    for fri in range(len(friend_list)):
        fri_list.insert(fri_tree, 0, text=friend_list[fri])

    # 添加群聊列表
    group_tree = fri_list.insert('', 1, 'second', text='群聊', values=("b"))
    for gro in range(len(group_list)):
        fri_list.insert(group_tree, 0, text=group_list[gro])


class creat_chat_thread(threading.Thread):   #继承父类threading.Thread
    def __init__(self, room_id):
        threading.Thread.__init__(self)
        self.self_room_id = room_id
        self.stat_encrypt = [0]
        self.stop = False

    def run(self):
        '''
        ！！！这个函数完全定义聊天界面！！！
        得到消息，将其他用户的消息发送到聊天框中，需要后端自己定义，
        并不了解如何随时刷新消息的机制,可能涉及到监听器什么的,所以这个函数接口可能也没用...
        :return:
        '''

        def getMsg(sender_name, now_time, msg):
            '''
            注释可看下一个函数
            聊天对象name储存在 usr_name 变量中
            此函数原目的为后端得到新消息并打印于聊天屏中
            :return:
            '''
            usr_name = sender_name
            t1_Msg.configure(state=tk.NORMAL)
            strMsg = '\n' + usr_name + now_time + '\n'
            t1_Msg.insert("end", strMsg, 'green')
            sendMsg = msg
            t1_Msg.insert("end", sendMsg)
            t2_sendMsg.delete('0.0', "end")
            t1_Msg.config(state=tk.DISABLED)
            print('\n' + '[recv]' + strMsg + ' >> ' + sendMsg + '\n')

        '''发送消息，将信息发送到聊天框中'''

        def sendMsg():
            '''
            按钮名为 sendMsg_btn，由于调用原因，位于大概206行位置
            t1_Msg.configure(state=tk.NORMAL) 和 t1_Msg.config(state=tk.DISABLED)应该是用来解除锁定和锁定消息框的，就是新消息的更新
            strMsg 和 t1_Msg.insert（） 状态信息，上面的绿字
            sendMsg 用于存放发出的信息
            t2_sendMsg.delete 应该是用于发送后删除输入框内的信息
            print(strMsg + sendMsg+'\n') 在t1上，也就是消息框内打印
            :return:
            '''
            t1_Msg.configure(state=tk.NORMAL)
            now_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            strMsg = '\n' + "我:" + now_time + '\n'
            # t1_Msg.insert("end", strMsg, 'green')
            sendMsg = t2_sendMsg.get('0.0', 'end')
            msg = sendMsg
            # t1_Msg.insert("end", sendMsg)
            t2_sendMsg.delete('0.0', "end")
            t1_Msg.config(state=tk.DISABLED)
            print('\n' + '[send]' + strMsg + ' >> ' + sendMsg + '\n')
            socket_send_msg(self.self_room_id, msg, self.stat_encrypt[0])
            # return time, msg
            '''下面部分用于消息显示于面板中，连接后端完成后应该删除'''

        def _quit():
            self.stop = True
            app.destroy()


            # 创建窗口
        app = tk.Toplevel(fri_form)
        app.protocol("WM_DELETE_WINDOW", _quit)
        app.title('聊天中')
        w = 800
        h = 660
        sw = app.winfo_screenwidth()
        sh = app.winfo_screenheight()
        x = (sw - w) / 2
        y = (sh - h) / 2
        app.geometry("%dx%d+%d+%d" % (w, h, x, y))
        app.resizable(0, 0)


        '''聊天消息预览窗口'''
        t1_Msg = tk.Text(app, width=113, height=32)
        b1 = tk.Scrollbar(app, width=40)
        b1.pack(side=tk.RIGHT, fill=tk.Y)
        b2 = tk.Listbox(app, yscrollcommand=b1.set)
        t1_Msg.tag_config('green', foreground='#008C00')  # 创建tag
        t1_Msg.place(x=2, y=35)
        # t1_Msg.config(state=tk.DISABLED)
        # t1_Msg.configure(state='disabled')

        '''聊天消息发送窗口'''
        t2_sendMsg = tk.Text(app, width=112, height=10)
        t2_sendMsg.place(x=2, y=485)


        def file_trans():
            '''
            文件传送按钮,按钮名file_btn，函数名file_trans
            文件传输对象储存在 path 变量中
            '''
            root = tk.Toplevel(app)

            root.title('路径选择')
            max_w, max_h = root.maxsize()
            root.geometry(f'500x300+{int((max_w - 500) / 2)}+{int((max_h - 300) / 2)}')  # 居中显示
            root.resizable(width=False, height=False)

            # 标签组件
            label = tk.Label(root, text='选择目录：', font=('华文彩云', 15))
            label.place(x=50, y=100)

            # 输入框控件
            entry_text = tk.StringVar()
            f = tk.Frame(root)
            s1 = tk.Scrollbar(f, orient=tk.HORIZONTAL)
            entry = tk.Entry(root, textvariable=entry_text, font=('FangSong', 10), width=30, state='readonly',
                             xscrollcommand=s1.set)
            entry.place(x=150, y=105)

            # 按钮控件
            def yes_path():
                root.destroy()

            def get_path():
                """注意，以下列出的方法都是返回字符串而不是数据流"""
                # 返回一个字符串，且只能获取文件夹路径，不能获取文件的路径。
                # path = filedialog.askdirectory(title='请选择一个目录')

                # 返回一个字符串，可以获取到任意文件的路径。
                path = filedialog.askopenfilename(title='请选择文件')

                # 生成保存文件的对话框， 选择的是一个文件而不是一个文件夹，返回一个字符串。
                # path = filedialog.asksaveasfilename(title='请输入保存的路径')
                print(path)
                entry_text.set(path)
                room_id = self.self_room_id
                socket_send_file(path, room_id)

            know_path = tk.Button(root, text='确定', command=yes_path)
            know_path.place(x=200, y=200)

            button = tk.Button(root, text='选择路径', command=get_path)
            button.place(x=400, y=95)
            root.mainloop()


        file_btn = tk.Button(app, text="文件传送", command=file_trans)
        file_btn.place(x=62, y=457)


        def voice_chat():
            '''
            调用录音机
            录音机文件为 recorder_old.py
            录音文件对象 储存在 fileName 中，
            为 ‘D:/recorder cache/testing.wav’
            试听完文件记得退出播放器进程
            :return:
            '''
            from chat.recorder_old import root
            from chat.recorder_old import fileName
            print(fileName)

        voice_chat_btn = tk.Button(app, text="语音", command=voice_chat)
        voice_chat_btn.place(x=660, y=457)


        ''' sendMsg_btn 点击操作函数为send_Msg, 位置约在95行'''
        sendMsg_btn = tk.Button(app, text="发送（Send）", command=sendMsg)
        sendMsg_btn.place(x=665, y=628)

        def encrypt_fun():
            '''
            加密状态变量名:stat_encrypt[0]，用列表是因为不用 global 定义全局变量
            :return:
            '''
            # stat_encrypt = stat
            # print(stat_encrypt[0])
            if self.stat_encrypt[0] == 0:
                # if True:
                box1 = messagebox.askyesno(title='询问', message='当前尚未加密，是否加密？')
                if box1 == True:
                    self.stat_encrypt[0] = 1
            else:
                box2 = messagebox.askyesno(title='询问', message='当前加密，是否解除加密？')
                if box2 == True:
                    self.stat_encrypt[0] = 0
            print("[前端] 一次一密 ==> ", self.stat_encrypt[0])

        chat_search_btn = tk.Button(app, text="一次一密加密开关", command=encrypt_fun)
        chat_search_btn.place(x=260, y=457)

        chat.client.gol.set_value(str(self.self_room_id) + "recv_msg_flag", 1)
        chat.client.gol.set_value(str(self.self_room_id) + "recv_msg_sender", -1)
        chat.client.gol.set_value(str(self.self_room_id) + "recv_msg_msg", "Welcome to BUPT-Chat!")

        all_his_msg = []
        all_his_msg = socket_get_room_history(self.self_room_id)
        user_info = chat.client.gol.get_value("user_info")
        for his_msg in all_his_msg:
            print("==============[Get history]=================")
            print("[sender_id]=>", his_msg[0])
            print("[time]=>", his_msg[1])
            print("[msg]=>", his_msg[2])
            print("inof =>", user_info)
            sender_username = "SYSTEM"
            for user in user_info:
                if user[0] == his_msg[0]:
                    sender_username = user[1]
            getMsg(sender_username, his_msg[1], his_msg[2])


        while True:
            if self.stop:
                break
            flag = chat.client.gol.get_value(str(self.self_room_id)+"recv_msg_flag")
            if flag == None:
                continue
            print("["+str(self.self_room_id)+"] recv_msg_flag ==>", flag)
            if flag == 1:
                chat.client.gol.set_value(str(self.self_room_id) + "recv_msg_flag", 0)
                print("[前端 ROOM:"+str(self.self_room_id)+"] =========================")
                sender_id = chat.client.gol.get_value(str(self.self_room_id) + "recv_msg_sender")
                sender_username = ""
                recv_msg = chat.client.gol.get_value(str(self.self_room_id) + "recv_msg_msg")
                print("id:" ,sender_id)
                room_info = chat.client.gol.get_value("user_info")
                if sender_id != -1:
                    for user in room_info:
                        if user[0] == sender_id:
                            sender_username = user[1]
                else:
                    sender_username = "SYSTEM"
                print("sender_id:" , sender_id)
                print("sender_name:" , sender_username)
                print("msg:" , recv_msg)
                getMsg(sender_username, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), recv_msg)
            time.sleep(0.1)


class fri_list_thread(threading.Thread):   #继承父类threading.Thread
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while True:
            list_change_flag = chat.client.gol.get_value("list_change_flag")
            if list_change_flag == 1:
                flush_list()
                print("[前端 List 线程] ==> Flush list")
                chat.client.gol.set_value("list_change_flag", 0)
            time.sleep(0.1)

# flush_list()
flt = fri_list_thread()
flt.start()

def double_selected(event):
    '''
    此函数定义好友列表双击事件
    两个print函数将分别打印usr_name与usr_id
    usr_name 为 item_text
    usr_id 为 chat_obj_id
    if 判断将忽略对总栏的双击行为
    :param event:
    :return:
    '''
    global friend_list
    global group_list
    for item in fri_list.selection():
        item_text = fri_list.item(item, "text")
        print('[前端 item_text] ==>', item_text)
        chat_obj_id = -1
        if item_text in friend_list:
            print("[前端 friend_list.index(item_text)] =>", friend_list.index(item_text))
            user_info = chat.client.gol.get_value("user_info")
            for user in user_info:
                if user[1] == item_text:
                    chat_obj_id = user[0]
                    friend_user_id = chat_obj_id
                    friend_user_id = [friend_user_id]
                    socket_send_userid(friend_user_id)
                    return

        if item_text in group_list:
            room_info = chat.client.gol.get_value("room_info")
            for room in room_info:
                if room[1] == item_text:
                    chat_obj_id = room[0]


        if item_text == '好友' or item_text == '群聊':
            continue

        room_id = chat_obj_id
        cct = creat_chat_thread(room_id)
        cct.start()
        # creat_chat()
        print("[前端 room_name] =>", item_text)
        print("[前端 room_id] =>", chat_obj_id)

fri_list.bind('<Double-1>', double_selected)
fri_list.pack(expand=True, fill=tk.X)


'''以下复选框'''
def mul_choose():
    global group_tree
    '''
    复选框的操作函数
    复选后的群聊成员在 member 列表中
    :return:
    '''
    # 复选框设置
    choose_wnd = tk.Toplevel(fri_form)
    choose_wnd.title('选择群聊中的联系人')
    choose_wnd.geometry("%dx%d+%d+%d" % (w, h, x, y))
    choose_wnd.resizable(0, 0)
    lab = tk.Label(choose_wnd, text='请选择好友来创建群聊', fg='blue', bg='lightblue', width=60)
    lab.grid(row=0, pady=2)

    # 创建群聊的函数，后端需要自行向数据库添加
    def creat_room(member, user_id):
        # global group_tree
        # fri_list.insert(group_tree, 0, text=member+'群聊')
        print("creat a room have " + member)
        print("creat a room have ", user_id)
        socket_send_userid(user_id)


    # 复选框选中，参数判定函数
    def print_info():
        select = ''
        select_id = []
        for j in var:
            if var[j].get():
                select = select + friend_list[j] + ' '
                select_id.append(friend_list_id[j])
        creat_room(select,select_id)
        choose_wnd.destroy()
        fri_form.update()

    # 复选框列表
    var = {}
    for i in range(len(friend_list)):
        var[i] = tk.BooleanVar()
        tk.Checkbutton(choose_wnd, text=friend_list[i], variable=var[i]).grid(row=i + 1)

    tk.Button(choose_wnd, text='确定', command=print_info).grid(pady=2)

def delete_group():
    global group_tree
    global group_list
    '''
    勾选删除成员的列表为 trash_member
    :return:
    '''
    choose_wnd = tk.Toplevel(fri_form)
    choose_wnd.title('选择群聊中的群组')
    choose_wnd.geometry("%dx%d+%d+%d" % (w, h, x, y))
    choose_wnd.resizable(0, 0)
    lab = tk.Label(choose_wnd, text='请选择要删除的群聊', fg='blue', bg='lightblue', width=60)
    lab.grid(row=0, pady=2)

    # 创建群聊的函数，后端需要自行向数据库添加
    def kill_group(trash_member):
        room_info = chat.client.gol.get_value("room_info")
        room_id_list = []
        for i in trash_member:
            group_list.remove(i)
            for room in room_info:
                if room[1] == i:
                    room_id_list.append(room[0])

        socket_send_rm_room(room_id_list)

        for row in fri_list.get_children():
            fri_list.delete(row)
        print(trash_member) # group_name list

        print(group_list)
        fri_tree = fri_list.insert('', 0, 'first', text='好友', values=('a'))
        for fri in range(len(friend_list)):
            fri_list.insert(fri_tree, 0, text=friend_list[fri])
        group_tree = fri_list.insert('', 1, 'second', text='群聊', values=("b"))
        for gro in range(len(group_list)):
            fri_list.insert(group_tree, 0, text=group_list[gro])

    # 复选框选中，参数判定函数
    def print_info():
        select = list()
        for j in var:
            if var[j].get():
                select.append(group_list[j])
        kill_group(select)
        choose_wnd.destroy()
        fri_form.update()

    # 复选框列表
    var = {}
    for i in range(len(group_list)):
        var[i] = tk.BooleanVar()
        tk.Checkbutton(choose_wnd, text=group_list[i], variable=var[i]).grid(row=i + 1)

    tk.Button(choose_wnd, text='确定', command=print_info).grid(pady=2)
    pass

delete_group_btn=tk.Button(fri_form, text="删除群聊", command=delete_group)
delete_group_btn.place(x=240, y=590)

mul_choose_btn = tk.Button(fri_form, text="发起群聊", command=mul_choose)
mul_choose_btn.place(x=310, y=590)

fri_form.mainloop()
